package api

import (
	"crypto/rand"
	"fmt"
	"lp/class"
	"lp/db"
	"lp/helpers"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/securecookie"
	log "github.com/jeanphorn/log4go"
)

var cookieHandler = securecookie.New(
	securecookie.GenerateRandomKey(64),
	securecookie.GenerateRandomKey(32))

//TokenGenerator 生成随机token
func TokenGenerator() string {
	b := make([]byte, 4)
	_, _ = rand.Read(b)
	return string(b)
}

//SetCookie 配置cookie相关
func SetCookie(username string, response http.ResponseWriter) {
	value := map[string]string{
		"name":  username,
		"token": TokenGenerator(),
	}

	if encoded, err := cookieHandler.Encode("cookie", value); err == nil {
		cookie := &http.Cookie{
			Name:  "cookie",
			Value: encoded,
			Path:  "/",
		}

		http.SetCookie(response, cookie)
	}
}

//ClearCookie 清除cookie
func ClearCookie(response http.ResponseWriter) {
	cookie := &http.Cookie{
		Name:   "cookie",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}

	http.SetCookie(response, cookie)
}

//GetUserName 从cookie读取用户信息
func GetUserName(request *http.Request) (username string) {
	if cookie, err := request.Cookie("cookie"); err == nil {
		cookieValue := make(map[string]string)
		if err = cookieHandler.Decode("cookie", cookie.Value, &cookieValue); err == nil {
			username = cookieValue["name"]
		}
	}

	return
}

const (
	//SIGNUPPORT 注册接口地址
	SIGNUPPORT = "/api/signup"
	//SIGNINPORT 登陆接口地址
	SIGNINPORT = "/api/signin"
	//UPLOADFILE 上传文件接口地址
	UPLOADFILE = "/api/uploadfile"
	//SIGNOUTPORT 登出接口地址
	SIGNOUTPORT = "/api/signout"
	//SERVERPORT 服务器端口
	SERVERPORT = ":5555"
)

///////////////////////////////////////接口/////////////////////////////////////////

//SignInHandler 登陆处理方式 for POST
func SignInHandler(w http.ResponseWriter, r *http.Request) {
	log.LOGGER("SERVER").Info("收到一个来自" + r.Host + "的登录请求")
	_ = r.ParseMultipartForm(32 << 20)

	//创建用户对象
	u := class.UserObj{}

	//绑定字段
	u.UserName = r.FormValue("username")
	u.Password = r.FormValue("password")
	u.Token = r.Header.Get("token")

	//要返回的提示信息
	msg := "登陆成功"
	code := http.StatusOK
	if !helpers.IsEmpty(u.UserName) && !helpers.IsEmpty(u.Password) {
		//内容不为空
		//检查用户是否存在
		isExist := db.IsExist(u)
		if !isExist {
			code = 400
			msg = "账户不存在"
		} else {
			//账户存在，校验密码与token
			ut, right := db.CheckUserInfo(u)
			if !right {
				code = 400
				msg = "账号或密码有误"
			} else {
				//所有校验通过
				u = ut
				//刷新最后活动时间
				u.LastActiveTime = strings.Split(time.Now().Local().String(), ".")[0]
				db.UpdateUserInfo(u, u.LastActiveTime, "last_active_time")
				//刷新token
				u.Token, _ = helpers.CreateToken(u.UserName, uint(u.ID), false)
				db.UpdateUserInfo(u, u.Token, "token")
				//刷新IP
				u.IP = r.RemoteAddr
				db.UpdateUserInfo(u, u.IP, "ip")
				//更新头像链接
				u.HeadURL = "http://" + r.Host + "/images/headpic/" + helpers.CutToken(u.Token) + ".png"
				db.UpdateUserInfo(u, u.HeadURL, "head_url")
				//重命名头像文件
				oldPicName := strings.Split(ut.HeadURL, "/")[5]
				p := helpers.HeadPicPath
				_ = helpers.RenameFile(p+oldPicName, p+helpers.CutToken(u.Token)+".png")
				//密码置为空
				u.Password = ""
			}
		}
	} else if !helpers.IsEmpty(u.Token) {
		//解析token并返回map
		jCC, err := helpers.ParseToken(u.Token)
		if err != nil {
			code = 400
			msg = "未知错误"
		} else {
			cl, _ := jCC["claims"].(map[string]interface{})
			u.UserName, _ = cl["iss"].(string)
			isExist := db.IsExist(u)
			if isExist {
				//Token校验通过
				//校验时间(与校验密码的方式一致)
				ut, ok := db.CheckUserInfo(u)
				if ok {
					u = ut
					//刷新最后活动时间
					u.LastActiveTime = strings.Split(time.Now().Local().String(), ".")[0]
					db.UpdateUserInfo(u, u.LastActiveTime, "last_active_time")
					//刷新token
					u.Token, _ = helpers.CreateToken(u.UserName, uint(u.ID), false)
					db.UpdateUserInfo(u, u.Token, "token")
					//刷新IP
					u.IP = r.RemoteAddr
					db.UpdateUserInfo(u, u.IP, "ip")
					//更新头像链接
					u.HeadURL = "http://" + r.Host + "/images/headpic/" + helpers.CutToken(u.Token) + ".png"
					db.UpdateUserInfo(u, u.HeadURL, "head_url")
					//重命名头像文件
					oldPicName := strings.Split(ut.HeadURL, "/")[5]
					p := helpers.HeadPicPath
					_ = helpers.RenameFile(p+oldPicName, p+helpers.CutToken(u.Token)+".png")
				} else {
					code = 400
					msg = "登陆验证失效，请用密码登陆"
					u.Token = ""
				}
				//密码置为空
				u.Password = ""
			} else {
				log.LOGGER("SERVER").Info("通过Token判断用户不存在")
				code = 400
				msg = "验证过期，登陆失败"
			}
		}

	} else {
		code = 400
		msg = "登陆信息不完整"
	}

	//返回消息
	w.WriteHeader(http.StatusOK)
	_, _ = fmt.Fprintln(w, helpers.CreateMsg(code, msg, u))
}

//SignUpHandler 注册处理方式 for POST
func SignUpHandler(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseMultipartForm(32 << 20)

	//创建用户对象
	u := class.UserObj{}

	//绑定字段
	u.UserName = r.FormValue("username")
	u.Password = r.FormValue("password")
	confirmPsw := r.FormValue("confirmPassword")
	u.Email = r.FormValue("email")
	u.Tel = r.FormValue("tel")

	//检查字段是否为空
	uName := !helpers.IsEmpty(u.UserName)
	psw := !helpers.IsEmpty(u.Password)
	uConfirmPsw := !helpers.IsEmpty(confirmPsw)
	uEmail := !helpers.IsEmpty(u.Email)
	uTel := !helpers.IsEmpty(u.Tel)

	//自动生成的字段
	now := strings.Split(time.Now().Local().String(), ".")[0]
	u.RegTime = now
	u.LastActiveTime = now
	u.IP = r.RemoteAddr

	//要返回的提示信息
	msg := "注册成功"
	code := 200
	if uName && uEmail && psw && uConfirmPsw && uTel && (u.Password == confirmPsw) {

		//所有字段都不为空
		//检查一些字段是否合法
		//检查邮箱格式
		pEmail := helpers.IsContentPass(u.Email, "email")
		if !pEmail {
			msg = "邮箱格式有误"
			code = 400
		} else {
			//检查号码格式
			pTel := helpers.IsContentPass(u.Tel, "tel")
			if !pTel {
				msg = "号码格式有误"
				code = 400
			} else {
				//检查密码
				pPsw := helpers.IsContentPass(u.Password, "password")
				if !pPsw {
					msg = "密码不符要求"
					code = 400
				} else {
					//检查数据库相关信息
					//检查用户是否存在
					isExist := db.IsExist(u)
					if isExist {
						msg = "注册失败，用户已存在"
						code = 400
					}
				}
			}
		}

	} else {
		msg = "注册失败，请检查注册信息"
		code = 400
	}

	if code == 200 {
		//通过所有注册验证验证
		//生成userID
		u.UserID = db.CreateUserID()
		//解算ID
		u.ID, _ = strconv.Atoi(strings.Split(u.UserID, "p")[1])
		//生成token
		u.Token, _ = helpers.CreateToken(u.UserName, uint(u.ID), false)
		//生成默认头像
		errMake := helpers.MakeHeadURL(helpers.CutToken(u.Token))
		if errMake != nil {
			log.LOGGER("SERVER").Error("生成用户头像失败:%v", errMake)
			msg = "注册失败"
			code = 400
		} else {
			//生成头像URL
			u.HeadURL = "http://" + r.Host + "/images/headpic/" + helpers.CutToken(u.Token) + ".png"
			//加入用户数据库
			right := db.InsertUser(u)
			if !right {
				u.Clear()
				msg = "注册失败，未知错误"
				code = 500
			}
		}

		//密码置为空
		u.Password = ""
	}

	//返回消息
	w.WriteHeader(http.StatusOK)
	_, _ = fmt.Fprintln(w, helpers.CreateMsg(code, msg, u))
}

//UploadHeadPicHandler 上传头像处理方式
func UploadHeadPicHandler(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseMultipartForm(32 << 20)

	//准备响应信息
	msg := "上传成功"
	code := 200

	//接收绑定上传的文件
	file, header, err := r.FormFile("file")
	if err != nil {
		log.LOGGER("SERVER").Error("接收文件出错%v", err)
		code = 400
		msg = "上传失败"
	}
	defer file.Close()

	u := class.UserObj{}

	fileInfo := r.FormValue("info")
	u.Token = r.Header.Get("token")

	if !helpers.IsEmpty(u.Token) {
		//解析token并返回map
		jCC, err := helpers.ParseToken(u.Token)
		if err != nil {
			code = 400
			msg = "未知错误"
		} else {
			//解析token成功
			cl, _ := jCC["claims"].(map[string]interface{})
			u.UserName, _ = cl["iss"].(string)
			isExist := db.IsExist(u)
			if isExist {
				//Token校验通过
				//校验时间(与校验密码的方式一致)
				ut, ok := db.CheckUserInfo(u)

				if ok {
					u = ut
					//刷新最后活动时间
					u.LastActiveTime = strings.Split(time.Now().Local().String(), ".")[0]
					db.UpdateUserInfo(u, u.LastActiveTime, "last_active_time")

					//保存文件
					var errSave error

					//根据token生成文件名
					s := strings.Split(header.Filename, ".")
					suffix := s[len(s)-1]
					//截取token最后一部分的前10个字符作为文件名
					fName := helpers.CutToken(u.Token) + "." + suffix

					//判断参数类型
					switch fileInfo {
					case "headpic":
						errSave = helpers.SaveFile(fName, helpers.HeadPicPath, file)
					default:
						errSave = helpers.SaveFile(fName, "./images/", file)
					}

					if errSave != nil {
						code = 400
						msg = "上传失败"
					}

				} else {
					code = 400
					msg = "上传失败"
					u.Token = ""
				}
			} else {
				log.LOGGER("SERVER").Info("通过Token判断用户不存在")
				code = 400
				msg = "上传失败"
			}
		}

	} else {
		code = 400
		msg = "上传失败"
	}

	//返回消息
	w.WriteHeader(http.StatusOK)
	_, _ = fmt.Fprintln(w, helpers.CreateMsg(code, msg, ""))
}

//SignOutHandler for POST
func SignOutHandler(response http.ResponseWriter, request *http.Request) {
	username := GetUserName(request)
	if !helpers.IsEmpty(username) {
		var indexBody, _ = helpers.LoadFile("templates/index.html")
		_, _ = fmt.Fprintf(response, indexBody, username)
	} else {
		http.Redirect(response, request, "/", 302)
	}
}
